package org.grade.filter;

import com.fasterxml.jackson.databind.ObjectMapper;
import org.grade.common.ServiceException;
import org.grade.model.LoginUser;
import org.grade.utils.JwtUtil;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Nonnull;
import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Objects;

import static org.grade.constant.RedisConstant.LOGIN_CODE_KEY;

/**
 * 处理 JWT身份验证的过滤器
 * 验证用户的 JWT 并将其信息存储在安全上下文中，以便后续请求可以访问这些信息。
 * 如果用户未登录或提供了无效的 token，则会抛出异常。
 */
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {
    @Resource
    private StringRedisTemplate stringRedisTemplate;//与redis交互


    /**
     * a. 从 HTTP 请求头中获取 token。
     * b. 如果没有找到 token，则允许请求继续（通过调用 filterChain.doFilter）。
     * c. 如果找到了 token，则尝试解析它并获取用户 ID。如果解析失败（例如，由于无效的 token），则抛出 ServiceException 异常。
     * d. 使用从 Redis 中获取的用户信息创建 LoginUser 对象。如果找不到用户信息（例如，用户未登录），则抛出另一个 ServiceException 异常。
     * e. 使用从 Redis 获取的用户信息创建一个新的 UsernamePasswordAuthenticationToken 对象，并将其设置为当前的安全上下文（通过 SecurityContextHolder）。这允许后续的请求访问此用户的信息和权限。
     * f. 最后，允许请求继续处理（再次调用 filterChain.doFilter）。
     * @param request
     * @param response
     * @param filterChain
     * @throws ServletException
     * @throws IOException
     */
    @Override
    protected void doFilterInternal(HttpServletRequest request, @Nonnull HttpServletResponse response, @Nonnull FilterChain filterChain) throws ServletException, IOException {
        // 获取token
        String token = request.getHeader("token");
        if (!StringUtils.hasText(token)) {
            // 没有token，放行
            filterChain.doFilter(request, response);
            return;
        }
        // 解析token
        String userId;
        try {
            userId = JwtUtil.parseJwt(token);
        } catch (Exception e) {
            throw new ServiceException("非法的token：" + token);
        }
        // 从redis中获取用户信息
        String userJson = stringRedisTemplate
                .opsForValue().get(LOGIN_CODE_KEY + userId);
        ObjectMapper objectMapper = new ObjectMapper();
        LoginUser loginUser = objectMapper.readValue(userJson, LoginUser.class);
        if (Objects.isNull(loginUser)) {
            throw new ServiceException("用户未登录");
        }
        // 存入SecurityContextHolder，设置用户授权信息
        UsernamePasswordAuthenticationToken authenticationToken =
                new UsernamePasswordAuthenticationToken(loginUser, null, loginUser.getAuthorities());
        SecurityContextHolder.getContext().setAuthentication(authenticationToken);
        // 放行
        filterChain.doFilter(request, response);
    }
}
